import { DATA } from './utils';

type EventCallback<K, V> = (event: K, data: V) => void;
type EventHandler<M> = Partial<{
	[K in keyof M]: EventCallback<K, M[K]>[];
}>;
type UnSubscriber = { unSubscribe(): void };

export class EventBus<M extends Record<string, unknown>> {
	private [DATA]: EventHandler<M> = {};
	subscribe<K extends keyof M>(event: K, callback: EventCallback<K, M[K]>) {
		const subscribers = this[DATA][event] ?? [];
		this[DATA][event] = subscribers;
		subscribers.push(callback);
		return {
			unSubscribe() {
				const idx = subscribers.findIndex((s) => s === callback);
				if (idx >= 0) {
					subscribers.splice(idx, 1);
				}
			},
		};
	}
	subcribeOnce<K extends keyof M>(
		event: K,
		callback: EventCallback<K, M[K]>,
		timeout?: number,
	): UnSubscriber {
		const revoker = this.subscribe(event, (callbackEvent, data) => {
			revoker.unSubscribe();
			callback(callbackEvent, data);
		});
		if (typeof timeout === 'number' && timeout >= 0) {
			setTimeout(() => {
				revoker.unSubscribe();
			}, timeout);
		}
		return revoker;
	}
	unsubscribeAll() {
		this[DATA] = {};
	}
	unsubscribe<K extends keyof M>(event: K, callback: EventCallback<K, M[K]>) {
		const subscribers = this[DATA][event];
		if (subscribers) {
			const idx = subscribers.findIndex((s) => s === callback);
			if (idx >= 0) {
				subscribers.splice(idx, 1);
			}
		}
	}

	send<K extends keyof M>(event: K, data: M[K]) {
		const subscribers = this[DATA][event];
		if (subscribers) {
			subscribers.forEach((s) => {
				s(event, data);
			});
		}
	}
	publish<K extends keyof M>(event: K, data: M[K], delay = 0): void {
		setTimeout(() => {
			this.send(event, data);
		}, delay);
	}
}
